import abc
import os
from unittest.mock import patch
from library_test.base_test import BaseLibraryTest


class TestProcessMcu(BaseLibraryTest, metaclass=abc.ABCMeta):
    TESTCASE_DIR = os.path.join(os.path.dirname(__file__), "testcase")

    @classmethod
    def get_module_path(cls):
        return "ascend_deployer.library.process_mcu"

    @classmethod
    def setUpClass(cls) -> None:
        super().setUpClass()

    def setUp(self) -> None:
        npu_info = {"card": 'A300i-pro', "model": 'A300i-pro', "scene": 'a300i', "product": 'A300i'}
        with patch('ascend_deployer.library.process_mcu.common_info.get_npu_info', return_value=npu_info):
            from ascend_deployer.library.process_mcu import McuInstallation

            self.utils = McuInstallation()


class TestProcessMcuGeneral(TestProcessMcu):

    @classmethod
    def get_testcase_path(cls):
        return os.path.join(cls.TESTCASE_DIR, "upgrade_mcu.yml")

    def test_find_mcu_file(self):
        exist_file = self.utils._find_mcu_file("/root/resources/run_from_a310p_zip/")
        self.assertTrue("Ascend-hdk-310p-mcu_24.2.1.hpm" in exist_file)
        empty_file = self.utils._find_mcu_file("/root/resources/")
        self.assertFalse(empty_file)

    def test_check_driver_and_npu(self):
        with self.assertRaises(Exception) as context:
            self.utils._check_driver_and_npu()
        self.assertEqual(str(context.exception),
                         "[ASCEND][WARNING] Driver not installed completely, please reinstall driver first")

    def test_run_command(self):
        err_return = 1
        err_command = 'err command'
        command_mock_out = 'out'
        command_mock_err = 'err'
        with patch.object(self.utils.module, 'run_command',
                          return_value=(err_return, command_mock_out, command_mock_err)):
            with self.assertRaises(Exception) as context:
                self.utils._run_command(err_command)
            self.assertEqual(str(context.exception), "calling {} failed on {}: {}".format(err_command, err_return,
                                                                                          command_mock_out + command_mock_err))

        ok_commamd = "ok command"
        command_mock_ok_out = "out"
        command_mock_ok_err = "err"
        ok_return = 0
        with patch.object(self.utils.module, 'run_command',
                          return_value=(ok_return, command_mock_ok_out, command_mock_ok_err)):
            output = self.utils._run_command(ok_commamd)
            self.assertEqual(output, command_mock_ok_out + command_mock_ok_err)

    def test_do_upgrade_mcu(self):
        npu_id_info = """                Total Count                    : 1

                NPU ID                         : 8
                Product Name                   : IT21PDDA01
                Serial Number                  : 033VNY10MB000071
                Chip Count                     : 1"""
        with patch.object(self.utils, '_run_command', return_value=npu_id_info):
            with self.assertRaises(Exception) as context:
                self.utils._do_upgrade_mcu()
        self.assertEqual(str(context.exception), "[ASCEND][WARNING] MCU upgrade failed, exit!")

    def test_process_mcu(self):
        # Test execution user is non-root --upgrade=mcu scenario
        @patch.object(os, 'getuid', return_value=-1)
        def non_root_test(mock_getuid):
            with self.assertRaises(Exception) as context:
                self.utils.auto_mode_flag = False
                self.utils._process_mcu()
            self.assertEqual(str(context.exception), '[ASCEND] None-root user cannot upgrade mcu!')
            with self.assertRaises(Exception) as context:
                self.utils.auto_mode_flag = False
                self.utils._process_mcu()
            self.assertEqual(str(context.exception), '[ASCEND] None-root user cannot upgrade mcu!')
            # Test execution user is non-root auto scenario
            with patch.object(self.utils.module, 'exit_json', return_value="skip"):
                self.utils.auto_mode_flag = True
                re = self.utils._process_mcu()
                self.assertEqual(re, "skip")

        non_root_test()

        # The test did not detect the auto scenario of the npu.
        with patch.object(self.utils.module, 'exit_json', return_value="skip"):
            self.utils.auto_mode_flag = True
            with patch.object(self.utils, 'npu_info', {}):
                result = self.utils._process_mcu()
                self.assertEqual(result, "skip")

        # The test did not detect the --upgrade =mcu scenario of the npu.
        with self.assertRaises(Exception) as context:
            self.utils.auto_mode_flag = False
            with patch.object(self.utils, 'npu_info', {}):
                self.utils._process_mcu()
        self.assertEqual(str(context.exception), "[ASCEND][WARNING] Can not detect npu, exit!")

        # Test the scenario without complete installation of npu
        with self.assertRaises(Exception) as context:
            self.utils.auto_mode_flag = False
            self.utils._process_mcu()
        self.assertEqual(str(context.exception),
                         "[ASCEND][WARNING] Driver not installed completely, please reinstall driver first")

        @patch.object(os.path, 'exists', return_value=None)
        def test_func_with_mock_path(mock_path):
            # The test did not find the auto scene of npu-smi
            with patch.object(self.utils.module, 'exit_json', return_value="skip"):
                self.utils.auto_mode_flag = True
                result = self.utils._process_mcu()
                self.assertEqual(result, "skip")

            # The test did not find the upgrade=mcu scenario of npu-smi
            self.utils.auto_mode_flag = False
            with self.assertRaises(Exception) as context:
                self.utils._process_mcu()
            self.assertEqual(str(context.exception), '[ASCEND][WARNING] Can not find npu-smi bin, exit!')

            # The test did not find the upgrade=mcu scenario for mcu.
            with patch.object(self.utils.module, 'get_bin_path', return_value=True):
                self.utils.auto_mode_flag = False
                with self.assertRaises(Exception) as context:
                    self.utils._process_mcu()
                self.assertEqual(str(context.exception), "[ASCEND][WARNING] Can not find mcu file, exit!")

            # Test the auto scene where the mcu file is not found.
            with patch.object(self.utils.module, 'get_bin_path', return_value=True):
                with patch.object(self.utils.module, 'exit_json', return_value="skip"):
                    self.utils.auto_mode_flag = True
                    result = self.utils._process_mcu()
                    self.assertEqual(result, "skip")

            @patch.object(self.utils.module, 'get_bin_path', return_value=True)
            @patch.object(self.utils, '_find_mcu_file', return_value="mcu.hpm")
            def test_func_with_mock_utils(mock_find, mock_get_bin):
                with self.assertRaises(Exception) as context:
                    self.utils.auto_mode_flag = False
                    self.utils._process_mcu()
                self.assertEqual(str(context.exception),
                                 "calling npu-smi info -l failed on -1: Testcase error, no cmd pattern found. cmd: ['npu-smi', 'info', '-l']")

            test_func_with_mock_utils()

        test_func_with_mock_path()
